﻿//**********************************************************
// MediaController
//**********************************************************
function MediaController(_mediaController)
{
	var _this = this;
	var _players = {};
	var _currentPlayer = null;
	var _services = {};
	var _host = OSH.getHost();
	var _mediaCache = {}; _mediaCache.length = 0;
	
	this.onPlayerStart = new Event();
	this.onPlayerExit = new Event();
	this.onPlayerChange = new Event();
	this.onMediaTreeChange = new Event();

	var _startSink = new EventSink(_mediaController, "MediaPlayerStart", onPlayerStartHandler);
	var _exitSink = new EventSink(_mediaController, "MediaPlayerExit", onPlayerExitHandler);
	var _mediaPlayerChangeSink = new EventSink(_mediaController, "ActiveMediaPlayerChange", onPlayerChangeHandler);

	function initialize()
	{
		var player = _mediaController.ActiveMediaPlayer;
		if(player && _this.getMediaLibraryNavigator().MoveToTreeWithDefaultPlayer(player))
			fireOnMediaTreeChange();
	}

	function onPlayerStartHandler(sender, args)
	{
		//window.top.log.writeln("Media player started.");
		fireOnPlayerStart();
	}

	function onPlayerExitHandler(sender, args)
	{
		//window.top.log.writeln("Media player exited.");
		fireOnPlayerExit();
	}
	
	function onPlayerChangeHandler(sender, args)
	{
		//window.top.log.writeln("Media player changed.");
		var oldPlayer = _currentPlayer;
		_currentPlayer = null;
		var newPlayer = getPlayerInternal(args.MediaPlayer, null);
		fireOnPlayerChange(oldPlayer, newPlayer);

		if(_this.getMediaLibraryNavigator().MoveToTreeWithDefaultPlayer(args.MediaPlayer))
			fireOnMediaTreeChange();
	}
	
	function fireOnPlayerStart()
	{
		var e = window.top.document.createEventObject();
		_this.onPlayerStart.fire(_this, e);
	}

	function fireOnPlayerExit()
	{
		var e = window.top.document.createEventObject();
		_this.onPlayerExit.fire(_this, e);
	}

	function fireOnPlayerChange(oldPlayer, newPlayer)
	{
		var e = window.top.document.createEventObject();
		e.oldPlayer = oldPlayer;
		e.newPlayer = newPlayer;
		_this.onPlayerChange.fire(_this, e);
	}

	function fireOnMediaTreeChange()
	{
		var e = window.top.document.createEventObject();
		_this.onMediaTreeChange.fire(_this, e);
	}

	function getPlayerInternal(player, playerName)
	{
		if(!playerName && player)
			playerName = player.Name;

		if(!playerName)
			return null;
		
		if(!_players[playerName])
		{
			if(!player)
			{
				player = _this.getManagedPlayerByName(playerName);
				if(!player)
					return null;
			}
			_players[playerName] = createPlayer(player, playerName);
		}
		
		return _players[playerName];
	}
	
	this.getCurrentPlayer = function()
	{
		if(_currentPlayer == null)
			_currentPlayer = getPlayerInternal(_mediaController.ActiveMediaPlayer, null);
		return _currentPlayer;
	}

	this.getPlayer = function(name)
	{
		return getPlayerInternal(null, name);
	}

	this.getManagedPlayerByName = function(name)
	{
		try { return _mediaController.GetMediaPlayerByName(name); }
		catch(e) { return null; }
	}

	this.registerPlayer = function(player)
	{
		_players[player.getName()] = player;
	}
	
	this.registerService = function(serviceName, serviceObj)
	{
		_services[serviceName] = serviceObj;
	}

	this.getService = function(serviceName)
	{
		return _services[serviceName];
	}

	this.getMediaLibraryNavigator = function()
	{
		return _mediaController.MediaLibraryNavigator;
	}
	
	this.getCurrentTreeNavigator = function()
	{
		return _mediaController.MediaLibraryNavigator.CurrentTreeNavigator;
	}

	this.getCurrentTreeInfo = function()
	{
		var result = { libraryName: null, treeName: null, folderName: null };

		var treeNav = this.getCurrentTreeNavigator();
		if(treeNav)
		{
			var folder = treeNav.CurrentFolder;
			if(folder)
			{
				result.folderName = folder.Name;

				var tree = folder.Tree;
				result.treeName = tree.Name;

				var lib = tree.Library;
				result.libraryName = lib.Name;
			}
		}

		return result;
	}

	this.nextMedia = function()
	{
		var treeNavigator = this.getCurrentTreeNavigator();
		if(treeNavigator && treeNavigator.MoveToNextMedia())
			this.playCurrentMedia()
	}

	this.previousMedia = function()
	{
		var treeNavigator = this.getCurrentTreeNavigator();
		if(treeNavigator && treeNavigator.MoveToPreviousMedia())
			this.playCurrentMedia()
	}

	this.nextFolder = function()
	{
		var treeNavigator = this.getCurrentTreeNavigator();
		if(treeNavigator && treeNavigator.MoveToNextFolder())
			this.playCurrentMedia()
	}

	this.previousFolder = function()
	{
		var treeNavigator = this.getCurrentTreeNavigator();
		if(treeNavigator && treeNavigator.MoveToPreviousFolder())
			this.playCurrentMedia()
	}

	this.nextTree = function()
	{
		var libraryNavigator = _mediaController.MediaLibraryNavigator;
		if(libraryNavigator.MoveToNextTree())
			this.playCurrentMedia()
	}

	this.previousTree = function()
	{
		var libraryNavigator = _mediaController.MediaLibraryNavigator;
		if(libraryNavigator.MoveToPreviousTree())
			this.playCurrentMedia()
	}

	this.moveToMediaByNumber = function(number)
	{
		var currentTreeNavigator = this.getCurrentTreeNavigator();
		if(!currentTreeNavigator) return false;

		var treeNavigator = currentTreeNavigator.Clone();
		if(treeNavigator.MoveToFirstMedia())
		{
			do
			{
				var media = treeNavigator.CurrentMedia;
				var channelNumber = media.Attributes.GetValue("ChannelNumber");
				if(channelNumber == number)
				{
					currentTreeNavigator.MoveTo(treeNavigator);
					return true;
				}
			}
			while(treeNavigator.MoveToNextMedia())
		}
		return false;
	}

	this.processCommand = function(sender, args)
	{
		var command = args.command;
		var commandLowerCase = command.toLowerCase();
		if(commandLowerCase == "next")
			this.nextMedia();
		else if(commandLowerCase == "previous")
			this.previousMedia();
		else if(commandLowerCase == "nextfolder")
			this.nextFolder();
		else if(commandLowerCase == "previousfolder")
			this.previousFolder();
		else if(commandLowerCase == "nexttree")
			this.nextTree();
		else if(commandLowerCase == "previoustree")
			this.previousTree();
		else
			return false;
		return true;
	}

	this.playCurrentMedia = function()
	{
		var media = _mediaController.MediaLibraryNavigator.CurrentTreeNavigator.CurrentMedia;
		if(media)
		{
			var player = this.getCurrentPlayer();
			if(player)
				player.play(media);
		}
	}

	function createPlayer(player, playerName)
	{
		var managedTypeFullName = OSH.getHost().GetTypeFullName(player);
		var type = MediaController._playerTypes[managedTypeFullName];
		if(!type)
			return new MediaPlayer(player, playerName);
		
		return new type(player, playerName)
	}

	this.getCurrentMedia = function()
	{
		var player = this.getCurrentPlayer();
		if(player)
			return player.getCurrentMedia();
		else
			return null;
	}

	this.getMedia = function(mediaUri, library)
	{
		if(!mediaUri) return null;

		var wrapper = _mediaCache[mediaUri];
		if(wrapper) return wrapper;

		if(!library)
			library = this.getMediaLibraryNavigator().CurrentLibrary;
		if(!library)
			return null;

		var media = library.GetMedia(mediaUri);
		return this.getMediaWrapper(media);
	}

	this.getMediaWrapper = function(media)
	{
		if(!media) return null;

		var uri = media.UriString;
		var wrapper = _mediaCache[uri];
		if(wrapper) return wrapper;

		if(_mediaCache.length > 1000)
		{
			_mediaCache = {};
			_mediaCache.length = 0;
		}
		
		wrapper = createMediaWrapper(media);
		_mediaCache[uri] = wrapper;
		_mediaCache.length++;
		return wrapper;
	}

	function createMediaWrapper(media)
	{
		var isChannel = _host.IsInterfaceImplementedBy(media, "Osh.Media.IChannel");
		if(isChannel)
			return new Channel(media);
		else
			return new Media(media);
	}

	initialize();
}

MediaController.registerPlayerType = function(managedTypeFullName, type)
{
	if(!MediaController._playerTypes)
		MediaController._playerTypes = {};
	
	MediaController._playerTypes[managedTypeFullName] = type;
}
Type.registerType('MediaController');


//**********************************************************
// MediaPlayer
//**********************************************************
function MediaPlayer(_player, _playerName)
{
	var _this = this;
	var _timeline;
	var _startSink;
	var _exitSink;
	var _mediaChangeSink;
	var _currentMedia;
	var _mediaController = OSH.getCurrent().mediaController;
	
	this.onStart = new Event();
	this.onExit = new Event();
	this.onMediaChange = new Event();

	function initialize()
	{
		_startSink = new EventSink(_player, "Start", function(s,a){ _this._onStart(s,a) });
		_exitSink = new EventSink(_player, "Exit", function(s,a){ _this._onExit(s,a) });
		_mediaChangeSink = new EventSink(_player, "MediaChange", function(s,a){ _this._onMediaChange(s,a) });
	}
	
	this._onStart = function(sender, args)
	{
		_currentMedia = null;
		
		window.top.log.writeln(_playerName + " Start");
		var e = window.top.document.createEventObject();
		this.onStart.fire(this, e);
	}

	this._onExit = function(sender, args)
	{
		window.top.log.writeln(_playerName + " Exit");
		var e = window.top.document.createEventObject();
		this.onExit.fire(this, e);

		_currentMedia = null;
	}
	
	this._onMediaChange = function(sender, args)
	{
		window.top.log.writeln(_playerName + " MediaChange");
		var e = window.top.document.createEventObject();
		_currentMedia = e.media = _mediaController.getMediaWrapper(args.Media);
		this.onMediaChange.fire(this, e);
	}

	this.getName = function()
	{
		return _playerName;
	}

	this.getCurrentMedia = function()
	{
		if(!_currentMedia)
			_currentMedia = _mediaController.getMediaWrapper(_player.CurrentMedia);
		return _currentMedia;
	}

	this.play = function(media)
	{
		if(!media)
			_player.Play();
		else if(media.getMedia)
			_player.PlayMedia(media.getMedia());
		else
			_player.PlayMedia(media);
	}

	this.pause = function()
	{
		_player.Pause();
	}

	this.stop = function()
	{
		_player.Stop();
	}

	this.next = function()
	{
		_player.Next();
	}

	this.previous = function()
	{
		_player.Previous();
	}

	this.getTimeline = function()
	{
		if(!_timeline)		
			_timeline = new Timeline(_player.Timeline);
		return _timeline;
	}

	this.processCommand = function(sender, args)
	{
		var command = args.command;
		var commandLowerCase = command.toLowerCase();
		if(commandLowerCase == "pause")
			this.pause();
		else if(commandLowerCase == "stop")
			this.stop();
		else if(commandLowerCase.left(4) == "play")
		{
//			var mediaUri = command.substring(5);
//			if(mediaUri && commandLowerCase.indexOf(":") == 4)
//				this.play(mediaUri);
//			else
				this.play(null);
		}
		else
			return false;
		return true;
	}
	
	initialize();
}
Type.registerType('MediaPlayer');


//**********************************************************
// Media
//**********************************************************
function Media(_media)
{
	var _attributes;
	
	this.uri = _media.UriString;
	this.name = _media.Name;
	
	function initialize()
	{
	}

	this.getMedia = function()
	{
		return _media;
	}

	this.reloadAttributes = function()
	{
		_attributes = null;
	}

	this.getAttributes = function()
	{
		if(!_attributes)
		{
			var jsonAttributes = _media.Attributes.ToJsonString();
			try
			{
				eval("_attributes=" + jsonAttributes + ";");
			}
			catch(e) {}
		}
		return _attributes;
	}

	this.getAttribute = function(attributeName)
	{
		return this.getAttributes()[attributeName];
	}
	
	initialize();
}
Type.registerType('Media');


//**********************************************************
// Channel
//**********************************************************
function Channel(_channel)
{
	Media.call(this, _channel);

	this.number = _channel.ChannelNumber;

	var _epg;

	this.getEpg = function()
	{
		if(!_epg)
			_epg = this._updateEpg();
		return _epg;
	}

	this.setChannelNumber = function(channelNumber)
	{
		try
		{
			if(!channelNumber) channelNumber = Object.Null;
			_channel.Attributes.SetValue("ChannelNumber", channelNumber);
			_channel.Library.Save();
			this.reloadAttributes();
			this.number = _channel.ChannelNumber;
		}
		catch(e) { }
	}

	this.getEpgForPeriod = function(startTime, endTime)
	{
		var epg = {};
		epg.events = [];
		epg.startTime = startTime;
		epg.endTime = endTime;

		var epgInfo = _channel.GetEpg(startTime.getVarDate(), endTime.getVarDate());

		var epgEvents = new Enumerator(epgInfo.Events).toArray();
		for(var i = 0; i < epgEvents.length; i++)
		{
			var epgEvent = new EpgEvent(epgEvents[i])
			epg.events.push(epgEvent);
		}
		return epg;
	}

	this._updateEpg = function() // protected virtual
	{
		var epg = {};
		epg.events = [];
		epg.startTime = new Date();
		epg.endTime = new Date(epg.startTime.valueOf() + (180 * 60 * 1000));

		var epgInfo = _channel.GetEpg(epg.startTime.getVarDate(), epg.endTime.getVarDate());

		var epgEvents = new Enumerator(epgInfo.Events).toArray();
		for(var i = 0; (i < epgEvents.length) && (i < 2); i++)
		{
			var epgEvent = new EpgEvent(epgEvents[i])
			epg.events.push(epgEvent);
		}
		return epg;
	}
}
Type.registerType('Channel', Media);


//**********************************************************
// EpgEvent
//**********************************************************
function EpgEvent(epgInfo)
{
    this.startTime = new Date(epgInfo.StartTime);
    this.endTime = new Date(epgInfo.EndTime);
    this.duration = epgInfo.Duration;
    this.name = epgInfo.Name;
    this.description = epgInfo.Description;
}
Type.registerType('EpgEvent');


//**********************************************************
// Proxy MediaPlayer
//**********************************************************
MediaPlayer.Proxy = function()
{
	var _this = this;
	var _mediaController = OSH.getCurrent().mediaController;
	var _player;
	var _timeline = Timeline.getProxy();
	
	// Common Media Events
	this.onStart = new Event();
	this.onExit = new Event();
	this.onMediaChange = new Event();

	// DVB Events
	this.onSignalChange = new Event();
	this.onRecordStart = new Event();
	this.onRecordProgress = new Event();
	this.onRecordStop = new Event();
	
	function initialize()
	{
		_mediaController.onPlayerChange.attach(this, onPlayerChange);
		_player = _mediaController.getCurrentPlayer();
		if(_player)
			attachToPlayer(_player);
	}
	
	function onPlayerChange(sender, e)
	{
		if(_player)
			detachFromPlayer(_player);

		_player = e.newPlayer;

		if(_player)
			attachToPlayer(_player);
	}

	function attachToPlayer(player)
	{
		window.top.log.writeln("MediaPlayer.Proxy.attachToPlayer('" + player.getName() + "')");
		player.onStart.attach(this, onStart);
		player.onExit.attach(this, onExit);
		player.onMediaChange.attach(this, onMediaChange);
		if(player.onSignalChange) player.onSignalChange.attach(this, onSignalChange);
		if(player.onRecordStart) player.onRecordStart.attach(this, onRecordStart);
		if(player.onRecordProgress) player.onRecordProgress.attach(this, onRecordProgress);
		if(player.onRecordStop) player.onRecordStop.attach(this, onRecordStop);
	}
	
	function detachFromPlayer(player)
	{
		window.top.log.writeln("MediaPlayer.Proxy.detachFromPlayer('" + player.getName() + "')");
		player.onStart.detach(this, onStart);
		player.onExit.detach(this, onExit);
		player.onMediaChange.detach(this, onMediaChange);
		if(player.onSignalChange) player.onSignalChange.detach(this, onSignalChange);
		if(player.onRecordStart) player.onRecordStart.detach(this, onRecordStart);
		if(player.onRecordProgress) player.onRecordProgress.detach(this, onRecordProgress);
		if(player.onRecordStop) player.onRecordStop.detach(this, onRecordStop);
	}
	
	function onStart(sender, e) { _this.onStart.fire(sender, e); }
	function onExit(sender, e) { _this.onExit.fire(sender, e); }
	function onMediaChange(sender, e) { _this.onMediaChange.fire(sender, e); }
	function onSignalChange(sender, e) { _this.onSignalChange.fire(sender, e); }
	function onRecordStart(sender, e) { _this.onRecordStart.fire(sender, e); }
	function onRecordProgress(sender, e) { _this.onRecordProgress.fire(sender, e); }
	function onRecordStop(sender, e) { _this.onRecordStop.fire(sender, e); }
	
	this.getPlayer = function() { return _player; }
	this.getTimeline = function() { return _timeline; }
	this.getCurrentMedia = function() { return _player ? _player.getCurrentMedia() : null; }
	this.play = function(media) { if(_player) _player.play(media); }
	this.pause = function() { if(_player) _player.pause(); }
	this.stop = function() { if(_player) _player.stop(); }

	//
	// DVB Player Methods
	//
	
	this.getSignalInfo = function() { return _player && _player.getSignalInfo ? _player.getSignalInfo() : null; }
	this.getRecordingInfo = function() { return _player && _player.getRecordingInfo ? _player.getRecordingInfo() : null; }
	this.getLastRecordingInfo = function() { return _player && _player.getLastRecordingInfo ? _player.getLastRecordingInfo() : null; }
	
	initialize();
}

MediaPlayer.getProxy = function()
{
	if(!MediaPlayer._proxy)
		MediaPlayer._proxy = new MediaPlayer.Proxy();
	return MediaPlayer._proxy;
}
Type.registerType('MediaPlayer.Proxy');


//**********************************************************
// Timeline
//**********************************************************
function Timeline(timelineInterface)
{
	var _this = this;
	var _timelineRefreshInterval = 900;
	var _timelineInterface = timelineInterface;
	var _timelineInfo;
	var _recordStartTime;
	var _inSeekingMode = false;
	var _virtualTime;
	var _seekSpeeds = [0, 10000, 60000, 300000, 600000];
	var _seekSpeed = 0;
	var _initialSeekSpeed = 1;
	var _timlineChangeEventSink;

	this.onProgress = new Event();

	function initialize()
	{
		_timlineChangeEventSink = new EventSink(_timelineInterface, "Change", onTimlineChange);
	}

	this.canSeek = function()
	{
		return _recordStartTime == null;
	}
	
	this.isInSeekingMode = function()
	{
		return _inSeekingMode;
	}
	
	this.startVirtualSeeking = function(forwardDirection)
	{
		this.changeVirtualSeekingSpeed(forwardDirection);
	}

	this.stopVirtualSeeking = function(changePosition)
	{
		if(!_inSeekingMode)
			return;
			
		var virtualTime = _virtualTime;
		_virtualTime = null;
		_inSeekingMode = false;
		_seekSpeed = 0;
		
		if(changePosition)
			_timelineInterface.SeekTo(virtualTime.getVarDate());

		updateTimelineInfoWithEvent(true, true);
	}

	this.pauseVirtualSeeking = function()
	{
		_seekSpeed = 0;
	}

	this.changeVirtualSeekingSpeed = function(forwardDirection)
	{
		if(!_inSeekingMode)
		{
			_inSeekingMode = true;
			_seekSpeed = (forwardDirection ? 1 : -1) * _initialSeekSpeed;
		}
		else
		{
			var newSeekSpeed = _seekSpeed + (forwardDirection ? 1 : -1);
			if(Math.abs(newSeekSpeed) < _seekSpeeds.length)
				_seekSpeed = newSeekSpeed;
		}

		//updateTimelineInfoWithEvent(true, false);
		if(_timelineInfo && _timelineInfo.virtualTime)
			updateNextVirtualTime(_timelineInfo);
		else
			updateTimelineInfo(true);
	}

	this.seekBy = function(offset)
	{
		_timelineInterface.SeekBy(offset);
	}
	
	this.seekTo = function(position)
	{
		_timelineInterface.SeekTo(position.getVarDate());
	}
	
	this.getTimelineInfo = function()
	{
		//updateTimelineInfoWithEvent(false, false);
		updateTimelineInfo(false);
		return _timelineInfo;
	}

	this.onRecordStart = function(recordingTime)
	{
		var timelineInfo = new Timeline.TimelineInfo(_timelineInterface);
		if(timelineInfo.currentTime)
		{
			if(recordingTime)
				_recordStartTime = timelineInfo.currentTime.subtract(recordingTime * 1000);
			else
				_recordStartTime = timelineInfo.currentTime;
		}
		this.stopVirtualSeeking(false);
	}

	this.onRecordStop = function()
	{
		_recordStartTime = null;
		updateTimelineInfoWithEvent(false, true);
	}
	
	function onTimlineChange(sender, args)
	{
		if(_this.onProgress.hasHandlers())
			updateTimelineInfoWithEvent(true, false);
	}

	function updateTimelineInfoWithEvent(forceUpdate, forceEventFire)
	{
		var changed = updateTimelineInfo(forceUpdate);
		if(changed || forceEventFire)
			fireOnProgress(_timelineInfo);
	}

	function updateTimelineInfo(forceUpdate)
	{
		// Check if the update can be skipped
		if(!forceUpdate && _timelineInfo)
			return false;

		var newTimelineInfo = new Timeline.TimelineInfo(_timelineInterface);

		var changed = (!_timelineInfo || _timelineInfo.hasChanged(newTimelineInfo));
		
		if(updateRecordTime(newTimelineInfo));
			changed = true;
		
		if(updateVirtualTime(newTimelineInfo))
			changed = true;

		_timelineInfo = newTimelineInfo;

		return changed;		
	}
	
	function updateRecordTime(newTimelineInfo)
	{
		var changed = (!_timelineInfo || Date.notEquals(_timelineInfo.recordStartTime, _recordStartTime));
		
		var newRecordStartTime = _recordStartTime;
		if(_recordStartTime)
		{
			if(newRecordStartTime < newTimelineInfo.startTime)
				newRecordStartTime = newTimelineInfo.startTime;
			else if(newRecordStartTime > newTimelineInfo.endTime)
				newRecordStartTime = newTimelineInfo.endTime;

			_recordStartTime = newRecordStartTime;
		}
		
		newTimelineInfo.recordStartTime = _recordStartTime;
		newTimelineInfo.recordStartPosition = (!_recordStartTime) ? null :
				Date.calcProgress(newTimelineInfo.startTime, newTimelineInfo.endTime, _recordStartTime);
		return changed;
	}
	
	function updateVirtualTime(newTimelineInfo)
	{
		if(!_inSeekingMode)
			return false;

		var changed = true;		
		if(!_virtualTime)
			_virtualTime = newTimelineInfo.currentTime;
		else
		{
			var direction = _seekSpeed >= 0 ? 1 : -1;
			var offset = direction * _seekSpeeds[Math.abs(_seekSpeed)];

			var newVirtualTime = _virtualTime;
			if(newVirtualTime)
				newVirtualTime = newVirtualTime.add(offset);

			if(newVirtualTime < newTimelineInfo.startTime)
				newVirtualTime = newTimelineInfo.startTime;
			else if(newVirtualTime > newTimelineInfo.endTime)
				newVirtualTime = newTimelineInfo.endTime;

			changed = (!_timelineInfo || Date.notEquals(_timelineInfo.virtualTime, newVirtualTime));
			_virtualTime = newVirtualTime;
		}
		
		newTimelineInfo.virtualTime = _virtualTime;
		newTimelineInfo.virtualPosition = (!_virtualTime) ? null :
				Date.calcProgress(newTimelineInfo.startTime, newTimelineInfo.endTime, _virtualTime);
		newTimelineInfo.nextVirtualTime = calcNextVirtualTime(newTimelineInfo);

		return changed;
	}
	
	function updateNextVirtualTime(timelineInfo)
	{
		if(!timelineInfo)
			timelineInfo = _timelineInfo;
		if(!timelineInfo)
			return;
		
		timelineInfo.nextVirtualTime = calcNextVirtualTime(timelineInfo);
	}
	
	function calcNextVirtualTime(timelineInfo)
	{
		if(!_inSeekingMode || !timelineInfo || !timelineInfo.virtualTime)
			return null;

		var direction = _seekSpeed >= 0 ? 1 : -1;
		var offset = direction * _seekSpeeds[Math.abs(_seekSpeed)];

		var nextVirtualTime = timelineInfo.virtualTime.add(offset);
		
		if(nextVirtualTime < timelineInfo.startTime)
			nextVirtualTime = timelineInfo.startTime;
		else if(nextVirtualTime > timelineInfo.endTime)
			nextVirtualTime = timelineInfo.endTime;
		return nextVirtualTime;
	}
	
	function fireOnProgress(timelineInfo)
	{
		var e = window.top.document.createEventObject();
		e.timelineInfo = timelineInfo;
		_this.onProgress.fire(_this, e);
	}

	initialize();	
}
Type.registerType('Timeline');


//**********************************************************
// Timeline
//**********************************************************
Timeline.TimelineInfo = function(timelineInterface)
{
	var _this = this;
	
	this.startTime = null;
	this.endTime = null;
	this.currentTime = null;
	this.currentPosition = 0;
	
	function initialize()
	{
		var timelineInfo = timelineInterface ? timelineInterface.CaptureState() : null;
		if(timelineInfo && timelineInfo.StartTime != null)
		{
			_this.startTime = new Date(timelineInfo.StartTime);
			_this.endTime = new Date(timelineInfo.EndTime);
			_this.currentTime = new Date(timelineInfo.CurrentTime);
			_this.currentPosition = Date.calcProgress(_this.startTime, _this.endTime, _this.currentTime);
		}
		else
		{
			_this.startTime = null;
			_this.endTime = null;
			_this.currentTime = null;
			_this.currentPosition = 0;
		}
	}

	this.hasChanged = function(timeline)
	{
		return Date.notEquals(this.startTime, timeline.startTime) ||
			Date.notEquals(this.endTime, timeline.endTime) ||
			Date.notEquals(this.currentTime, timeline.currentTime);
	}
	
	this.toString = function()
	{
		return Date.format(this.startTime, "u") + " - " +
			Date.format(this.endTime, "u") + " / " +
			Date.format(this.currentTime, "u") + " (" + this.currentPosition + ")";
	}
	
	initialize();
}
Type.registerType('Timeline.TimelineInfo');


//**********************************************************
// Proxy Timeline
//**********************************************************
Timeline.Proxy = function()
{
	var _this = this;
	var _mediaController = OSH.getCurrent().mediaController;
	var _player;
	
	this.onProgress = new Event();
	
	function initialize()
	{
		_mediaController.onPlayerChange.attach(this, onPlayerChange);
		_player = _mediaController.getCurrentPlayer();
		if(_player)
			attachToPlayer(_player);
	}
	
	function onPlayerChange(sender, e)
	{
		if(_player)
			detachFromPlayer(_player);

		_player = e.newPlayer;

		if(_player)
			attachToPlayer(_player);
	}

	function attachToPlayer(player)
	{
		window.top.log.writeln("Timeline.Proxy.attachToPlayer('" + player.getName() + "')");
		var timeline = player.getTimeline();
		timeline.onProgress.attach(this, onProgressHandler);
	}
	
	function detachFromPlayer(player)
	{
		window.top.log.writeln("Timeline.Proxy.detachFromPlayer('" + player.getName() + "')");
		var timeline = player.getTimeline();
		timeline.onProgress.detach(this, onProgressHandler);
	}
	
	function onProgressHandler(sender, e)
	{
		//window.top.log.writeln("Timeline.Proxy.onProgressHandler('" + e.timelineInfo.toString() + "')");
		_this.onProgress.fire(sender, e);
	}
	
	this.getTimeline = function()
	{
		return _player ? _player.getTimeline() : null;
	}
	
	this.getTimelineInfo = function()
	{
		var timeline = this.getTimeline();
		return timeline ? timeline.getTimelineInfo() : new Timeline.TimelineInfo();
	}

	initialize();
}

Timeline.getProxy = function()
{
	if(!Timeline._proxy)
		Timeline._proxy = new Timeline.Proxy();
	return Timeline._proxy;
}
Type.registerType('Timeline.Proxy');

